from django.db import transaction
from django.db.models import F
from django.utils import timezone
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from ERP_5.utils.cont import NumberPrefix
from ERP_5.utils.generate_code import generate_code
from ERP_5.utils.get_inventory import get_inventory_by_goods
from basic_info.models import WarehouseModel
from goods_info.models import GoodsModel, GoodsInventoryModel
from production_info.models import ProductProcessModel, ProductMaterialModel, ProductTaskModel
from warehouse_info.models import SaleDeliverModel, SaleDeliverItemModel, PurchaseStorageModel, PurchaseStorageItemModel


class ProductProcessSerializer(serializers.ModelSerializer):
    """
    生产计划中的：生产工序 序列化器
    """

    class Meta:
        model = ProductProcessModel
        fields = '__all__'


class ProductMaterialSerializer(serializers.ModelSerializer):
    """
    生产计划中的：生产物料 序列化器
    """
    cur_inventory = serializers.SerializerMethodField(read_only=True, help_text='在仓库中库存')

    class Meta:
        model = ProductMaterialModel
        fields = '__all__'

    def get_cur_inventory(self, obj: ProductMaterialModel):  # 当前的obj为：ProductMaterialModel
        result = get_inventory_by_goods(obj.goods.id)
        return result if result else 0


class ProductTaskSerializer(serializers.ModelSerializer):
    """
    生产任务的 序列化器
    """

    # 当前生产任务中所有的： 生产工序列表
    process_list = ProductProcessSerializer(many=True)

    # 当前生产任务中所有的： 生产物料列表
    material_list = ProductMaterialSerializer(many=True)

    class Meta:
        model = ProductTaskModel
        fields = '__all__'

    def create(self, validated_data):
        process_list = validated_data.pop('process_list')
        material_list = validated_data.pop('material_list')
        with transaction.atomic():
            pt = ProductTaskModel.objects.create(**validated_data)
            for item in process_list:
                ProductProcessModel.objects.create(product_task=pt, **item)
            for item in material_list:
                ProductMaterialModel.objects.create(product_task=pt, **item)
        return pt

    def update(self, instance, validated_data):
        process_list = []
        material_list = []
        if instance.status == '2':
            raise ValidationError("生产任务已经完成，不能修改!")
        if 'process_list' in validated_data:
            process_list = validated_data.pop('process_list')
        if 'material_list' in validated_data:
            material_list = validated_data.pop('material_list')
        old_list1 = instance.process_list
        old_list2 = instance.material_list
        with transaction.atomic():
            if process_list and old_list1.exists():
                # 然后把旧数据删除，因为在validated_data拿不到货品库存数据的ID
                instance.process_list.all().delete()
            if material_list and old_list2.exists():
                # 然后把旧数据删除，因为在validated_data拿不到货品库存数据的ID
                instance.material_list.all().delete()
            for item in process_list:
                ProductProcessModel.objects.create(product_task=instance, **item)
            for item in material_list:
                ProductMaterialModel.objects.create(product_task=instance, **item)
            result = super(ProductTaskSerializer, self).update(instance=instance, validated_data=validated_data)
        return result


class GetMaterialSerializer(serializers.Serializer):
    """
    领料的序列化器
    """

    def update(self, instance, validated_data):
        pass

    def create(self, validated_data):
        # 需要完成4件事情
        with transaction.atomic():
            # 1、插入出库单
            deliver = SaleDeliverModel.objects.create(invoices_date=timezone.now(),
                                                      number_code=generate_code(NumberPrefix['deliver'].value),
                                                      last_amount=validated_data['money'],
                                                      number_count=validated_data['count'],
                                                      product_task_id=validated_data['pt_id'],
                                                      status='1',
                                                      is_other='1')
            # 2、插入出库项目
            goods = GoodsModel.objects.get(pk=validated_data['good_id'])
            wh = WarehouseModel.objects.get(pk=validated_data['warehouse_id'])
            psi = SaleDeliverItemModel()
            psi.goods_id = goods.id
            psi.specification = goods.specification
            psi.model_number = goods.model_number
            psi.number_code = goods.number_code
            psi.color = goods.color
            psi.category = goods.category
            psi.category_name = goods.category.name
            psi.units = goods.units
            psi.units_name = goods.units.basic_name
            psi.name = goods.name

            psi.sale_count = validated_data['count']
            psi.sale_price = validated_data['price']
            psi.sale_money = validated_data['money']
            psi.warehouse_name = wh.name
            psi.warehouse_id = wh.id

            psi.sale_deliver_id = deliver.id
            psi.save()

            # 3、 修改库存数量
            GoodsInventoryModel.objects \
                .filter(goods_id=goods.id, warehouse_id=wh.id) \
                .update(cur_inventory=F('cur_inventory') - psi.sale_count)
            # 4、 修改生产物料中领料数量
            ProductMaterialModel.objects.filter(id=validated_data['material_id']) \
                .update(get_count=F('get_count') + psi.sale_count)

            return deliver

    goods_number_code = serializers.CharField(help_text='物料的条码或者编号', required=False)
    warehouse_name = serializers.CharField(help_text='领取仓库的名字')
    warehouse_id = serializers.IntegerField(help_text='领取仓库的主键')
    good_id = serializers.IntegerField(help_text='领取的货品主键')

    count = serializers.DecimalField(help_text='领取数量,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)
    price = serializers.DecimalField(help_text='领取的成本单价,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)
    money = serializers.DecimalField(help_text='领取的成本总价,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)

    material_id = serializers.IntegerField(help_text='所属生产物料的主键')
    pt_id = serializers.IntegerField(help_text='所属生产任务的主键')


class BackMaterialSerializer(serializers.Serializer):
    """
    退料的序列化器
    """

    def update(self, instance, validated_data):
        pass

    def create(self, validated_data):
        # 需要完成4件事情
        with transaction.atomic():
            # 1、插入 入库单
            storage = PurchaseStorageModel.objects.create(invoices_date=timezone.now(),
                                                          number_code=generate_code(NumberPrefix['storage'].value),
                                                          last_amount=validated_data['money'],
                                                          number_count=validated_data['count'],
                                                          product_task_id=validated_data['pt_id'],
                                                          status='1',
                                                          is_other='1')
            # 2、插入 入库单中的项目
            goods = GoodsModel.objects.get(pk=validated_data['good_id'])
            wh = WarehouseModel.objects.get(pk=validated_data['warehouse_id'])
            psi = PurchaseStorageItemModel()
            psi.goods_id = goods.id
            psi.specification = goods.specification
            psi.model_number = goods.model_number
            psi.number_code = goods.number_code
            psi.color = goods.color
            psi.category = goods.category
            psi.category_name = goods.category.name
            psi.units = goods.units
            psi.units_name = goods.units.basic_name
            psi.name = goods.name

            psi.purchase_count = validated_data['count']
            psi.purchase_price = validated_data['price']
            psi.purchase_money = validated_data['money']
            psi.warehouse_name = wh.name
            psi.warehouse_id = wh.id

            psi.purchase_storage_id = storage.id
            psi.save()

            # 3、 修改库存数量
            GoodsInventoryModel.objects \
                .filter(goods_id=goods.id, warehouse_id=wh.id) \
                .update(cur_inventory=F('cur_inventory') + psi.purchase_count)
            # 4、 修改生产物料中退料数量
            ProductMaterialModel.objects.filter(id=validated_data['material_id']) \
                .update(back_count=F('back_count') + psi.purchase_count)

            return storage

    goods_number_code = serializers.CharField(help_text='物料的条码或者编号', required=False)
    warehouse_name = serializers.CharField(help_text='退还仓库的名字', required=False)
    warehouse_id = serializers.IntegerField(help_text='退还仓库的主键')
    good_id = serializers.IntegerField(help_text='退还的货品主键')

    count = serializers.DecimalField(help_text='退还数量,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)
    price = serializers.DecimalField(help_text='退还的成本单价,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)
    money = serializers.DecimalField(help_text='退还的成本总价,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)

    material_id = serializers.IntegerField(help_text='所属生产物料的主键')
    pt_id = serializers.IntegerField(help_text='所属生产任务的主键')


class ProductStorageSerializer(serializers.Serializer):
    """
    生产入库的序列化器
    """

    def update(self, instance, validated_data):
        pass

    def create(self, validated_data):
        # 需要完成4件事情
        with transaction.atomic():
            # 1、插入 入库单
            storage = PurchaseStorageModel.objects.create(invoices_date=timezone.now(),
                                                          number_code=generate_code(NumberPrefix['storage'].value),
                                                          last_amount=validated_data['money'],
                                                          number_count=validated_data['count'],
                                                          product_task_id=validated_data['pt_id'],
                                                          status='1',
                                                          is_other='1')
            # 2、插入 入库单中的项目
            goods = GoodsModel.objects.get(pk=validated_data['good_id'])
            wh = WarehouseModel.objects.get(pk=validated_data['warehouse_id'])
            psi = PurchaseStorageItemModel()
            psi.goods_id = goods.id
            psi.specification = goods.specification
            psi.model_number = goods.model_number
            psi.number_code = goods.number_code
            psi.color = goods.color
            psi.category = goods.category
            psi.category_name = goods.category.name
            psi.units = goods.units
            psi.units_name = goods.units.basic_name
            psi.name = goods.name

            psi.purchase_count = validated_data['count']
            psi.purchase_price = validated_data['price']
            psi.purchase_money = validated_data['money']
            psi.warehouse_name = wh.name
            psi.warehouse_id = wh.id

            psi.purchase_storage_id = storage.id
            psi.save()

            # 3、 修改库存数量
            GoodsInventoryModel.objects \
                .filter(goods_id=goods.id, warehouse_id=wh.id) \
                .update(cur_inventory=F('cur_inventory') + psi.purchase_count)
            # 4、 修改生产任务的：已入库数量
            ProductTaskModel.objects.filter(id=validated_data['pt_id']) \
                .update(storage_number=F('storage_number') + psi.purchase_count)

            return storage

    goods_number_code = serializers.CharField(help_text='入库商品的条码或者编号', required=False)
    good_id = serializers.IntegerField(help_text='入库的货品主键')
    warehouse_name = serializers.CharField(help_text='入库仓库的名字', required=False)
    warehouse_id = serializers.IntegerField(help_text='入库仓库的主键')

    count = serializers.DecimalField(help_text='入库数量,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)
    price = serializers.DecimalField(help_text='成本单价,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)
    money = serializers.DecimalField(help_text='入库成本总价,最多精确到小数点后两位', max_digits=10, decimal_places=2, default=0)

    pt_id = serializers.IntegerField(help_text='所属生产任务的主键')
